package grpc

import (
	"fmt"
	"log"
	"time"

	"gitee.com/zawei/yggdrasil/pkg/utils/xnet"
	"gitee.com/zawei/yggdrasil/pkg/utils/xtls"
	"google.golang.org/grpc"
)

type Config struct {
	Host         string
	Port         uint64
	Network      string
	EnableTrace  bool
	EnableMetric bool
	TLS          *xtls.SSLConfig

	// 慢日志阈值
	SlowThreshold time.Duration

	serverOptions      []grpc.ServerOption
	streamInterceptors []grpc.StreamServerInterceptor
	unaryInterceptors  []grpc.UnaryServerInterceptor
	serviceDesc        []struct {
		svr  interface{}
		desc *grpc.ServiceDesc
	}
}

// WithServerOption inject grpcServer option to grpc grpcServer
// User should not inject interceptor option, which is recommend by WithStreamInterceptor
// and WithUnaryInterceptor
func (c *Config) WithServerOption(opts ...grpc.ServerOption) *Config {
	if c.serverOptions == nil {
		c.serverOptions = make([]grpc.ServerOption, 0, len(opts))
	}
	c.serverOptions = append(c.serverOptions, opts...)
	return c
}

// WithStreamInterceptor inject stream interceptors to grpcServer option
func (c *Config) WithStreamInterceptor(intes ...grpc.StreamServerInterceptor) *Config {
	if c.streamInterceptors == nil {
		c.streamInterceptors = make([]grpc.StreamServerInterceptor, 0, len(intes))
	}

	c.streamInterceptors = append(c.streamInterceptors, intes...)
	return c
}

// WithUnaryInterceptor inject unary interceptors to grpcServer option
func (c *Config) WithUnaryInterceptor(intes ...grpc.UnaryServerInterceptor) *Config {
	if c.unaryInterceptors == nil {
		c.unaryInterceptors = make([]grpc.UnaryServerInterceptor, 0, len(intes))
	}

	c.unaryInterceptors = append(c.unaryInterceptors, intes...)
	return c
}

func (c *Config) Address() string {
	return fmt.Sprintf("%s:%d", c.Host, c.Port)
}

func (c *Config) Build() *grpcServer {
	var err error
	c.Host, err = xnet.Extract(c.Host)
	if err != nil {
		log.Fatalf("fault to create grpc grpcServer, error: %s", err.Error())
	}
	if c.Network == "" {
		c.Network = "tcp"
	}

	c.WithUnaryInterceptor(
		errorsUnaryServerInterceptor,
		mdUnaryServerInterceptor,
		logUnaryServerInterceptor(c.SlowThreshold),
	)

	c.WithStreamInterceptor(
		errorsStreamServerInterceptor,
		mdStreamServerInterceptor,
		logStreamServerInterceptor(c.SlowThreshold),
	)

	if c.EnableTrace {
		c.WithUnaryInterceptor(traceUnaryServerInterceptor)
		c.WithStreamInterceptor(traceStreamServerInterceptor)
	}

	if c.EnableMetric {
		c.WithUnaryInterceptor(metricUnaryServerInterceptor)
		c.WithStreamInterceptor(metricStreamServerInterceptor)
	}
	c.serviceDesc = make([]struct {
		svr  interface{}
		desc *grpc.ServiceDesc
	}, 0)
	return newServer(c)
}
